package fr.emse.tatiana;






import java.io.File;
import java.util.Vector;


import org.eclipse.core.resources.*;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Platform;

import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.ITreeSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.DragSource;
import org.eclipse.swt.dnd.DragSourceEvent;
import org.eclipse.swt.dnd.DragSourceListener;
import org.eclipse.swt.dnd.DropTarget;
import org.eclipse.swt.dnd.DropTargetEffect;
import org.eclipse.swt.dnd.DropTargetEvent;
import org.eclipse.swt.dnd.DropTargetListener;
import org.eclipse.swt.dnd.FileTransfer;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IEditorReference;
import org.eclipse.ui.IPartListener;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.model.WorkbenchContentProvider;
import org.eclipse.ui.part.ResourceTransfer;

import fr.emse.tatiana.commands.AddFileAction;
import fr.emse.tatiana.commands.ApplyFilterAction;
import fr.emse.tatiana.commands.CreateFolderAction;
import fr.emse.tatiana.commands.CreateReplayableAction;
import fr.emse.tatiana.commands.DeleteCorpusAction;
import fr.emse.tatiana.commands.DeleteFileAction;
import fr.emse.tatiana.commands.ExportAction;
import fr.emse.tatiana.commands.NewGraphAnalysisAction;
import fr.emse.tatiana.commands.RenameFileAction;
import fr.emse.tatiana.corpus.*;
import fr.emse.tatiana.display.BadDisplayFile;
import fr.emse.tatiana.display.DisplayFileReader;
import fr.emse.tatiana.extensionprovider.IAnalysisActivator;
import fr.emse.tatiana.remotecontrol.RemoteControl;
import fr.emse.tatiana.replayable.CreatedReplayableInput;
import fr.emse.tatiana.replayable.IArtefactEditor;
import fr.emse.tatiana.replayable.ReplayableInput;
import fr.emse.tatiana.replayable.ReplayableModel;
import fr.emse.tatiana.replayable.analysis.GraphAnalysis;
import fr.emse.tatiana.replayable.analysis.GraphAnalysisEditor;
import fr.emse.tatiana.replayable.analysis.GraphAnalysisInput;
import fr.emse.tatiana.replayable.tabulardisplay.ReplayableTabularEditor;

/**
 * Implementation of a view on a corpus. In function of the currently selected object, 
 * sets the enablement of actions.
 * 
 * Chooses how to open a file on double-click
 * 
 * @author dyke
 *
 */
public class CorpusNavigationView extends AbstractNavigationView implements SelectionListener {
	public static final String ID = "fr.emse.tatiana.navigationView";
	
	
	
	
	
	private IProject currentProject;

	private RemoteControl remote;

	/**
	 * Only shows Tatiana corpuses (doesn't show CVS, files that start with "." 
	 * and the Tatiana folder)
	 * 
	 * @author dyke
	 *
	 */
	protected class TatianaWorkbenchContentProvider extends WorkbenchContentProvider {

		Object current;

		@Override
		public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
			super.inputChanged(viewer, oldInput, newInput);
			current = newInput;
		}

		@Override
		protected void processDelta(IResourceDelta delta) {
			PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() {
				public void run() {
					CorpusNavigationView.this.viewer.refresh(true);
				}
			});
		}

		@Override
		public Object getParent(Object element) { // if children are children of Tatiana, show them further down
			IResource parent = (IResource) super.getParent(element);
			if (parent instanceof IProject && parent.getName().equals(TatianaConstants.TATIANA_PROJECT_NAME)) { 
				return super.getParent(parent);
			}
			return parent;
		}

		@Override
		public Object[] getChildren(Object parent) {
			Object[] children = super.getChildren(parent);
			Vector<Object> ret = new Vector<Object>();

			if (((IResource) parent).getType() == IResource.ROOT) { // don't show Tatiana as project
				for (int i=0; i<children.length; i++) {
					IResource child = (IResource) children[i];
					IProject tatiana = null;
					if (child.getName().equals(TatianaConstants.TATIANA_PROJECT_NAME)) {
						tatiana = (IProject) child;
					} else {
						ret.add(child);
					}
					// uncomment this to add in tatiana subfolders
//					if (tatiana!=null) {
//					ret.add(tatiana.getFolder("filters"));
//					ret.add(tatiana.getFolder("layouts"));
//					ret.add(tatiana.getFolder("scripts"));
//					}
				}
			} else { // don't show hidden files or CVS folders
				for (int i=0; i<children.length; i++) {
					IResource child = (IResource) children[i];
					if (!(
							child.getName().startsWith(".") ||
							child.getName().equalsIgnoreCase("CVS")
					)) {
						ret.add(child);
					}
				}
			}

			return ret.toArray();
		}
	}


	/**
	 * Add the Replay button to the viewer
	 */
	@Override
	public void createPartControl(Composite parent) {
		super.createPartControl(parent);
		GridData griddata = new GridData(GridData.FILL,GridData.FILL,true,true);
		viewer.getTree().setLayoutData(griddata);
		Button replaybutton = new Button(parent, SWT.PUSH);
		replaybutton.setText("Synchronize");
		replaybutton.addSelectionListener(this);
		
		int ops = DND.DROP_MOVE;
		Transfer[] transfers = new Transfer[] { ResourceTransfer.getInstance()};
		/*
		viewer.addDragSupport(ops, transfers,new MyDragListener(viewer));
		viewer.addDropSupport(ops, transfers,new MyDropListener(viewer));
		*/
		DragSource source=new DragSource(viewer.getControl(), ops);
		source.setTransfer(transfers);
		source.addDragListener(new DragSourceListener(){

			public void dragFinished(DragSourceEvent event) {
				// TODO Auto-generated method stub
				
			}

			public void dragSetData(DragSourceEvent event) {
				// TODO Auto-generated method stub
				/*
				if(ResourceTransfer.getInstance().isSupportedType(event.dataType)){
					System.out.println("blabla");
				}
				*/
			}

			public void dragStart(DragSourceEvent event) {
				IStructuredSelection sel=(IStructuredSelection) viewer.getSelection();
				IPath path = ((IResource) sel.getFirstElement()).getLocation();
				IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(path.lastSegment());
				if (project.exists()) {
					event.doit=false;
				}
				if(path.lastSegment().equals("analyses")){
					event.doit=false;
				}
				if(path.lastSegment().equals("traces")){
					event.doit=false;
				}
				if(path.lastSegment().equals("replayables")){
					event.doit=false;
				}
			}
			
		});
		DropTarget target=new DropTarget(viewer.getControl(), ops);
		target.setTransfer(transfers);
		target.addDropListener(new DropTargetListener(){

			public void dragEnter(DropTargetEvent event) {
				// TODO Auto-generated method stub
				
			}

			public void dragLeave(DropTargetEvent event) {
				// TODO Auto-generated method stub
				
			}

			public void dragOperationChanged(DropTargetEvent event) {
				// TODO Auto-generated method stub
				
			}

			public void dragOver(DropTargetEvent event) {
				// TODO Auto-generated method stub
				
			}

			public void drop(DropTargetEvent event) {
			

			}

			public void dropAccept(DropTargetEvent event) {
				// TODO Auto-generated method stub
			}
			
		});
		
		
		createContextMenu();
		
		
	}

	
	private void createContextMenu() { 
		
		MenuManager menuMgr=new MenuManager(); 
		menuMgr.setRemoveAllWhenShown(true); 

		menuMgr.addMenuListener(new IMenuListener() { 
			public void menuAboutToShow(IMenuManager mgr) {
				
				final MenuManager newreplayable = new MenuManager("New Replayable");
		        newreplayable.add(ApplicationActionBarAdvisor.instance.applyFilterAction);
		        newreplayable.add(ApplicationActionBarAdvisor.instance.createReplayableAction);
		        final MenuManager newanalysis = new MenuManager("New Analysis");
		        newanalysis.add(ApplicationActionBarAdvisor.instance.newGraphAction);
		        for (IAction action : ApplicationActionBarAdvisor.instance.newAnalysisActions) {
		        	action.setEnabled(true);
		        	newanalysis.add(action);
		        }
		        
		        final MenuManager newvisualisation = new MenuManager("Visualise as");
		        for (IAction action : ApplicationActionBarAdvisor.instance.newVisualisationActions) {
		        	newvisualisation.add(action);
		        }
		        
		       
				mgr.add(ApplicationActionBarAdvisor.instance.newCorpusAction);
				mgr.add(new Separator());
				mgr.add(ApplicationActionBarAdvisor.instance.createFolderAction);
				mgr.add(ApplicationActionBarAdvisor.instance.deleteFileAction);
				mgr.add(ApplicationActionBarAdvisor.instance.addFileAction);
				mgr.add(ApplicationActionBarAdvisor.instance.renameFileAction);
				mgr.add(new Separator());
				mgr.add(newreplayable);
				mgr.add(newanalysis);
				mgr.add(newvisualisation);
				mgr.add(new Separator());
				mgr.add(ApplicationActionBarAdvisor.instance.importAction);
				mgr.add(ApplicationActionBarAdvisor.instance.exportAction);
				mgr.add(ApplicationActionBarAdvisor.instance.deleteCorpusAction);
				
			}
		}); 
		// Create menu. 
		Menu menu = menuMgr.createContextMenu(viewer.getControl()); 
		viewer.getControl().setMenu(menu); 
		// Registermenu for extension. 

		getSite().registerContextMenu(menuMgr, viewer);
	}


	
	
	public void addCorpus(Corpus c) { // doesn't actually add, it replaces
//		corpora = new Vector<Corpus>();
//		corpora.add(c);
//		viewer.setInput(corpora);
		viewer.expandAll();
	}

	/**
	 * @return the corpus in which we have the current selection
	 */
	public Corpus getSelectedCorpus() {
		return new Corpus(this.currentProject);
	}
	
	
	/* (non-Javadoc)
	 * @see org.eclipse.jface.viewers.ISelectionChangedListener#selectionChanged(org.eclipse.jface.viewers.SelectionChangedEvent)
	 */
	
	public void selectionChanged(SelectionChangedEvent event) {
		Vector<IAction> actions = new Vector<IAction>();
		actions.add(ApplyFilterAction.getInstance());
		actions.add(AddFileAction.getInstance());
		actions.add(DeleteCorpusAction.getInstance());
		actions.add(NewGraphAnalysisAction.getInstance());
		actions.add(CreateReplayableAction.getInstance());
		actions.add(CreateFolderAction.getInstance());
		actions.add(RenameFileAction.getInstance());
		actions.add(ExportAction.getInstance());
		actions.add(DeleteFileAction.getInstance());
		actions.addAll(ApplicationActionBarAdvisor.replayableSelectedContingentActions);
		actions.addAll(ApplicationActionBarAdvisor.navigationViewContingentActions);
		for (IAction action: actions) {
			action.setEnabled(false);
		}

		IStructuredSelection s = (IStructuredSelection) event.getSelection();
		if (s.size() > 1) {
			// TODO z later something clever about selecting more than one corpus or zero
		}
		if (s.isEmpty()) {
			this.currentProject = null;
			return; 
		}
		IResource r = (IResource) s.getFirstElement();
		if (!r.getProject().isOpen()) {
			try {
				r.getProject().open(null);
			} catch (CoreException e1) {
				// shouldn't happen
				e1.printStackTrace();
			}
		}
		this.currentProject = r.getProject();

		if (!r.getParent().getName().equals(TatianaConstants.REPLAYABLES_FOLDER_NAME)) {
			actions.removeAll(ApplicationActionBarAdvisor.replayableSelectedContingentActions);
		}

		for (IAction action: actions) {
			action.setEnabled(true);
		}
	}
	
	/* (non-Javadoc)
	 * @see org.eclipse.jface.viewers.IDoubleClickListener#doubleClick(org.eclipse.jface.viewers.DoubleClickEvent)
	 */
	public void doubleClick(DoubleClickEvent event) {
		try {
			IFile replayable = (IFile) ((ITreeSelection) event.getSelection()).getFirstElement();
			String type = null;
			if (!replayable.getParent().getName().equals(TatianaConstants.TRACES_FOLDER_NAME)) {
				// Trace files automatically get type null
				try {
					DisplayFileReader reader = new DisplayFileReader(replayable.getLocation().toString());
					type = reader.getType();
				} catch (Exception e) {
					// wasn't a display file
				}
			}

			IEditorPart part = null;
			if (GraphAnalysis.DISPLAY_TYPE.equals(type)) {
				IEditorInput input = new GraphAnalysisInput(replayable);
				part = getSite().getWorkbenchWindow().getActivePage().openEditor(input, GraphAnalysisEditor.ID);
			} else if (ReplayableInput.DISPLAY_TYPE.equals(type)) {
				IEditorInput input = new ReplayableInput(replayable);
				part = this.getSite().getWorkbenchWindow().getActivePage().openEditor(input, fr.emse.tatiana.replayable.tabulardisplay.ReplayableTabularViewer.ID);
			} else if (ReplayableModel.DISPLAY_TYPE.equals(type)) {
				IEditorInput input = new CreatedReplayableInput(replayable);
				part = this.getSite().getWorkbenchWindow().getActivePage().openEditor(input, ReplayableTabularEditor.ID);
			} else if (type != null) {
				try {
					IConfigurationElement[] config = Platform.getExtensionRegistry()
							.getConfigurationElementsFor("fr.emse.tatiana.analysis");
					for (IConfigurationElement e : config) {
						if (!e.getAttribute("displayType").equals(type))
							continue;
						Object o = e.createExecutableExtension("analysisActivator");
						if (o instanceof IAnalysisActivator) {
							IAnalysisActivator activator = (IAnalysisActivator) o;
							IEditorInput input = activator.getInput(replayable);
							part = this.getSite().getWorkbenchWindow().getActivePage().openEditor(input, e.getAttribute("editorID"));
						}
					}
				} catch (Exception ex) {
					System.out.println(ex.getMessage());
				}
			} else {
				IEditorInput input = new CorpusFileEditor.CorpusFileInput(new CorpusFile(replayable));
				part = this.getSite().getWorkbenchWindow().getActivePage().openEditor(input, CorpusFileEditor.ID);
			}	
			if (part == null) {
				System.err.println("unknown display type: " + type);
			}
		} catch (ClassCastException e) {
			ISelection selection = viewer.getSelection();
			Object obj = (Object) ((IStructuredSelection) selection).getFirstElement();
			if (obj == null) return;
			if(!viewer.getExpandedState(obj)){
				viewer.expandToLevel(obj, 1);
			}
			else{
				viewer.collapseToLevel(obj,1);
			}
		} catch (BadDisplayFile e) {
			MessageDialog.openError(this.getSite().getShell(), "Could not open file", "The file you selected could not be opened: "+e);
			e.printStackTrace();
		} catch (PartInitException e) {
			MessageDialog.openError(this.getSite().getShell(), "Could not open file", "The file you selected could not be opened: "+e);
			e.printStackTrace();
		} catch (CoreException e) {
			MessageDialog.openError(this.getSite().getShell(), "Could not open file", "The file you selected could not be opened: "+e);
			e.printStackTrace();
		}
	}

	public void widgetDefaultSelected(SelectionEvent e) {
		// do nothing
	}


	public void addArtefact(IArtefactEditor ed) {
		if (remote != null && !remote.getEnd()) {
			remote.addDiscreteReplayer(ed);
			ed.setRemote(remote);
		}
	}

	public void addRemoteArtefact(CorpusFileEditor cfe) {
		if (remote != null && !remote.getEnd()) {
			cfe.startSynchronisation(remote);
		}
	}

	private boolean islistener = false;
	public void widgetSelected(SelectionEvent e) {
		if (remote == null || remote.getEnd()) {
			remote = new RemoteControl();
		} else {
			return;
		}


		if (!islistener)  {
			islistener = true;
			this.getSite().getWorkbenchWindow().getPartService().addPartListener(new IPartListener() {



				public void partOpened(IWorkbenchPart ed) {
					if (ed instanceof IArtefactEditor) {
						addArtefact((IArtefactEditor)ed);
					} else if (ed instanceof CorpusFileEditor) {
						addRemoteArtefact((CorpusFileEditor) ed);
					}
				}

				public void partActivated(IWorkbenchPart part) {
					// TODO Auto-generated method stub

				}

				public void partBroughtToTop(IWorkbenchPart part) {
					// TODO Auto-generated method stub

				}

				public void partClosed(IWorkbenchPart part) {
					// TODO Auto-generated method stub

				}

				public void partDeactivated(IWorkbenchPart part) {
					// TODO Auto-generated method stub

				}


			});
		}

		IEditorReference[] editors = this.getSite().getWorkbenchWindow().getActivePage().getEditorReferences();
		for (int i=0 ; i<editors.length;i++) {
			IEditorPart ed = editors[i].getEditor(false);
			if (ed instanceof IArtefactEditor) {
				addArtefact((IArtefactEditor) ed);
			} else if (ed instanceof CorpusFileEditor) { 
				// TODO soon choose replayer from a list
				addRemoteArtefact((CorpusFileEditor)ed);
			}
		}
		remote.start();
		// wait for the remote control to be ready
	}

	public IProject getSelectedProject() {
		return this.currentProject;
	}


	@Override
	protected WorkbenchContentProvider getContentProvider() {
		return new TatianaWorkbenchContentProvider();
	}


	public RemoteControl getRemote() {
		return remote;
	}

}